using System;
using System.IO;
using System.Numerics;
using OracleInternal.Json.Common;
using OracleInternal.Json.Sql;

namespace OracleInternal.Json.Binary
{
	internal class OracleJsonSerializer : OracleJsonGenerator
	{
		internal const char MIN_HIGH_SURROGATE = '\ud800';

		internal const char MAX_HIGH_SURROGATE = '\udbff';

		internal const char RUBOUT = '\u007f';

		internal IJsonOutput m_writer;

		internal StreamContext m_streamingCtx;

		private static string s_HexDigitPairs;

		static OracleJsonSerializer()
		{
			s_HexDigitPairs = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF";
		}

		internal OracleJsonSerializer(Stream stream)
		{
			m_writer = new UTF8JsonOutput(stream);
			m_streamingCtx = new StreamContext(GetExceptionFactory());
		}

		internal OracleJsonSerializer(TextWriter writer)
		{
			m_writer = new WriterJsonOutput(writer);
			m_streamingCtx = new StreamContext(GetExceptionFactory());
		}

		public override IOracleJsonGenerator WriteStartObject()
		{
			WriteSeparator();
			m_streamingCtx.StartObject();
			WriteAscii('{');
			return this;
		}

		public override IOracleJsonGenerator WriteStartArray()
		{
			WriteSeparator();
			m_streamingCtx.StartArray();
			WriteAscii('[');
			return this;
		}

		public override IOracleJsonGenerator WriteEnd()
		{
			if (m_streamingCtx.InObject())
			{
				m_streamingCtx.End();
				WriteAscii('}');
			}
			else
			{
				m_streamingCtx.End();
				WriteAscii(']');
			}
			return this;
		}

		public override IOracleJsonGenerator Write(string value)
		{
			Primitive();
			WriteQuotedString(value, m_writer, GetExceptionFactory());
			return this;
		}

		public override IOracleJsonGenerator Write(char[] value, int strLen)
		{
			Primitive();
			WriteQuotedString(value, strLen, m_writer, GetExceptionFactory());
			return this;
		}

		public override IOracleJsonGenerator WriteKey(string key)
		{
			WriteSeparator();
			m_streamingCtx.PendingKey();
			WriteQuotedString(key, m_writer, GetExceptionFactory());
			WriteAscii(':');
			return this;
		}

		internal static void WriteQuotedString(string value, IJsonOutput m_writer, IExceptionFactory f)
		{
			try
			{
				WriteEscaped(value, m_writer);
			}
			catch (IOException ex)
			{
				throw OracleJsonExceptions.IO.Create(f, ex);
			}
		}

		internal static void WriteQuotedString(char[] value, int strLen, IJsonOutput m_writer, IExceptionFactory f)
		{
			try
			{
				WriteEscaped(value, strLen, m_writer);
			}
			catch (IOException ex)
			{
				throw OracleJsonExceptions.IO.Create(f, ex);
			}
		}

		internal static string SerializeString(string value)
		{
			StringWriter stringWriter = new StringWriter();
			WriterJsonOutput writerJsonOutput = new WriterJsonOutput(stringWriter);
			WriteQuotedString(value, writerJsonOutput, null);
			try
			{
				writerJsonOutput.Close();
			}
			catch (IOException ex)
			{
				throw new IllegalStateException(ex.Message);
			}
			return stringWriter.ToString();
		}

		public override IOracleJsonGenerator Write(decimal value)
		{
			Primitive();
			WriteBigDecimal(value);
			return this;
		}

		public override IOracleJsonGenerator Write(BigInteger value)
		{
			Primitive();
			WriteBigInteger(value);
			return this;
		}

		public override IOracleJsonGenerator Write(int value)
		{
			Primitive();
			WriteInt(value);
			return this;
		}

		public override IOracleJsonGenerator Write(long value)
		{
			Primitive();
			WriteLong(value);
			return this;
		}

		public override IOracleJsonGenerator Write(double value)
		{
			AssertFinite(value);
			Primitive();
			WriteDouble(value);
			return this;
		}

		public override IOracleJsonGenerator Write(float value)
		{
			AssertFinite(value);
			Primitive();
			WriteFloat(value);
			return this;
		}

		public override IOracleJsonGenerator Write(bool value)
		{
			Primitive();
			WriteBoolean(value);
			return this;
		}

		public override IOracleJsonGenerator WriteNull()
		{
			Primitive();
			WriteNullInternal();
			return this;
		}

		public override void Close()
		{
			m_streamingCtx.Close();
			try
			{
				m_writer.Close();
			}
			catch (IOException ex)
			{
				throw OracleJsonExceptions.IO.Create(GetExceptionFactory(), ex);
			}
		}

		protected IExceptionFactory GetExceptionFactory()
		{
			return OracleJsonExceptions.ORACLE_FACTORY;
		}

		public override void Flush()
		{
			try
			{
				m_writer.Flush();
			}
			catch (IOException ex)
			{
				throw OracleJsonExceptions.IO.Create(GetExceptionFactory(), ex);
			}
		}

		protected override IOracleJsonGenerator WriteBinary(OracleJsonBinary value)
		{
			return Write(value.GetJsonString());
		}

		protected override IOracleJsonGenerator WriteDouble(OracleJsonDouble value)
		{
			return WriteNumber(value);
		}

		protected override IOracleJsonGenerator WriteFloat(OracleJsonFloat value)
		{
			return WriteNumber(value);
		}

		protected override IOracleJsonGenerator WriteOraNumber(OracleJsonDecimal value)
		{
			return WriteNumber(value);
		}

		internal IOracleJsonGenerator WriteNumber(OracleJsonNumber value)
		{
			Primitive();
			try
			{
				m_writer.WriteAscii(NumberToString(value));
				return this;
			}
			catch (IOException ex)
			{
				throw OracleJsonExceptions.IO.Create(GetExceptionFactory(), ex);
			}
		}

		internal string NumberToString(OracleJsonNumber n)
		{
			if (n != null)
			{
				return n.GetJsonString();
			}
			return n.ToJsonString();
		}

		protected override IOracleJsonGenerator WriteTimestamp(OracleJsonTimestamp value)
		{
			return Write(value.GetJsonString());
		}

		protected override IOracleJsonGenerator WriteTimestampTZ(OracleJsonTimestampTZ value)
		{
			return Write(value.GetJsonString());
		}

		protected override IOracleJsonGenerator WriteDate(OracleJsonDate value)
		{
			return Write(value.GetJsonString());
		}

		protected override IOracleJsonGenerator WriteIntervalDS(OracleJsonIntervalDS value)
		{
			return Write(value.GetJsonString());
		}

		protected override IOracleJsonGenerator WriteIntervalYM(OracleJsonIntervalYM value)
		{
			return Write(value.GetJsonString());
		}

		protected override IOracleJsonGenerator WriteString(OracleJsonString value)
		{
			return Write(value.GetCharArrRep(), value.GetStringLen());
		}

		internal void WriteAscii(string s)
		{
			try
			{
				m_writer.WriteAscii(s);
			}
			catch (IOException ex)
			{
				throw OracleJsonExceptions.IO.Create(GetExceptionFactory(), ex);
			}
		}

		internal void WriteBigDecimal(decimal value)
		{
			WriteAscii(value.ToString());
		}

		private void WriteBigInteger(BigInteger value)
		{
			WriteAscii(value.ToString());
		}

		internal void WriteInt(int value)
		{
			WriteAscii(value.ToString());
		}

		internal void WriteLong(long value)
		{
			WriteAscii(value.ToString());
		}

		internal void WriteFloat(float value)
		{
			WriteAscii(value.ToString());
		}

		internal void WriteDouble(double value)
		{
			WriteAscii(value.ToString());
		}

		internal void WriteBoolean(bool value)
		{
			WriteAscii(value.ToString().ToLower());
		}

		internal void WriteNullInternal()
		{
			WriteAscii("null");
		}

		internal void WriteAscii(char c)
		{
			try
			{
				m_writer.WriteAscii((byte)c);
			}
			catch (IOException ex)
			{
				throw OracleJsonExceptions.IO.Create(GetExceptionFactory(), ex);
			}
		}

		internal void Primitive()
		{
			WriteSeparator();
			m_streamingCtx.Primitive();
		}

		internal void WriteSeparator()
		{
			if (m_streamingCtx.HasChildren() && !m_streamingCtx.m_pendingKey)
			{
				WriteAscii(',');
			}
		}

		internal static void EscapeUnicode(int codePoint, IJsonOutput m_writer)
		{
			m_writer.WriteAscii("\\u");
			m_writer.WriteAscii(s_HexDigitPairs, ((codePoint >> 8) & 0xFF) * 2, 2);
			m_writer.WriteAscii(s_HexDigitPairs, (codePoint & 0xFF) * 2, 2);
		}

		internal static void WriteEscapedChar(char c, IJsonOutput m_writer)
		{
			switch (c)
			{
			case '\n':
				m_writer.WriteAscii("\\n");
				break;
			case '\r':
				m_writer.WriteAscii("\\r");
				break;
			case '\t':
				m_writer.WriteAscii("\\t");
				break;
			case '\f':
				m_writer.WriteAscii("\\f");
				break;
			case '\\':
				m_writer.WriteAscii("\\\\");
				break;
			case '"':
				m_writer.WriteAscii("\\\"");
				break;
			default:
				EscapeUnicode(c, m_writer);
				break;
			}
		}

		internal static void WriteEscaped(string value, IJsonOutput m_writer)
		{
			bool flag = true;
			int num = 0;
			int num2 = 0;
			m_writer.WriteAscii('"');
			int i;
			for (i = 0; i < value.Length; i++)
			{
				char c = value[i];
				if (c < ' ' || c == '\\' || c == '"' || c == '\u007f')
				{
					num = i - num2;
					if (!flag)
					{
						m_writer.Write(value, num2, num);
					}
					else if (num > 0)
					{
						m_writer.WriteAscii(value, num2, num);
					}
					num2 = i + 1;
					flag = true;
					WriteEscapedChar(c, m_writer);
				}
				else
				{
					if (c > '\u007f')
					{
						flag = false;
					}
					num = i - num2;
				}
			}
			num = i - num2;
			if (!flag)
			{
				m_writer.Write(value, num2, num);
			}
			else if (num > 0)
			{
				m_writer.WriteAscii(value, num2, num);
			}
			m_writer.WriteAscii('"');
		}

		internal static void WriteEscaped(char[] value, int strLen, IJsonOutput m_writer)
		{
			bool flag = true;
			int num = 0;
			int num2 = 0;
			m_writer.WriteAscii('"');
			int i;
			for (i = 0; i < strLen; i++)
			{
				char c = value[i];
				if (c < ' ' || c == '\\' || c == '"' || c == '\u007f')
				{
					num = i - num2;
					if (!flag)
					{
						m_writer.Write(value, num2, num);
					}
					else if (num > 0)
					{
						m_writer.WriteAscii(value, num2, num);
					}
					num2 = i + 1;
					flag = true;
					WriteEscapedChar(c, m_writer);
				}
				else
				{
					if (c > '\u007f')
					{
						flag = false;
					}
					num = i - num2;
				}
			}
			num = i - num2;
			if (!flag)
			{
				m_writer.Write(value, num2, num);
			}
			else if (num > 0)
			{
				m_writer.WriteAscii(value, num2, num);
			}
			m_writer.WriteAscii('"');
		}

		internal void AssertFinite(double value)
		{
			if (double.IsInfinity(value) || double.IsNaN(value))
			{
				throw new FormatException(value.ToString());
			}
		}

		private static void Escape(int cp, IJsonOutput writer)
		{
			char[] array = cp.ToString().ToCharArray();
			for (int i = 0; i < array.Length; i++)
			{
				string text = Convert.ToByte(array[i]).ToString("X").ToUpper();
				writer.WriteAscii("\\u");
				for (int j = 0; j < 4 - text.Length; j++)
				{
					writer.WriteAscii(48);
				}
				writer.WriteAscii(text);
			}
		}

		public override IOracleJsonGenerator Write(string key, DateTime value)
		{
			WriteKey(key);
			Write(value);
			return this;
		}

		public override IOracleJsonGenerator Write(DateTime value)
		{
			DateTime local = value.ToUniversalTime();
			byte[] bytes = OsonPrimitiveConversions.ToOracleTimestamp(GetExceptionFactory(), local);
			Write(OsonPrimitiveConversions.TimestampToString(GetExceptionFactory(), bytes));
			return this;
		}

		public override IOracleJsonGenerator Write(string key, byte[] value)
		{
			WriteKey(key);
			Write(value);
			return this;
		}

		public override IOracleJsonGenerator Write(byte[] value)
		{
			return Write(OracleJsonBinary.GetJsonString(value, isId: false));
		}

		public override IOracleJsonGenerator WriteId(byte[] value)
		{
			return Write(OracleJsonBinary.GetJsonString(value, isId: true));
		}

		public override IOracleJsonGenerator Write(Period value)
		{
			return Write(OracleJsonIntervalYM.SerializePeriod(value, GetExceptionFactory()));
		}

		public override IOracleJsonGenerator Write(TimeSpan value)
		{
			return Write(OracleJsonIntervalDS.SerializeDuration(value, GetExceptionFactory()));
		}
	}
}
